module anem_csv_mod

  use csv_module
  use anem_mod
  use datetime
  use string
  use container
  use flogger
  use regex
  use params_mod
  use data_translators_utils_mod
  use cli_mod

  implicit none

  private

  public anem_csv_read

contains

  subroutine anem_csv_read(file_path, towers, records, mute)

    character(*), intent(in) :: file_path
    type(hash_table_type), intent(inout), target :: towers
    type(linked_list_type), intent(inout), target :: records
    logical, intent(in), optional :: mute

    type(anem_tower_type), pointer :: tower
    type(anem_record_type), pointer :: record
    type(datetime_type) time
    type(csv_file) f
    logical status_ok
    integer i, idot, j
    character(10), allocatable :: columns(:)
    character(30), allocatable :: time_col(:)
    character(10), allocatable :: sid_col(:)
    real(8), allocatable :: lon_col(:)
    real(8), allocatable :: lat_col(:)
    real(8), allocatable :: elv_col(:)
    real(8), allocatable :: ws_col(:)
    real(8), allocatable :: wd_col(:)
    real(8), allocatable :: ta_col(:)
    real(8), allocatable :: p_col(:)
    real(8), allocatable :: rh_col(:)
    logical, allocatable :: consumed(:)
    integer i_ws, i_wd, i_ta, i_p, i_rh
    real h

    ! time,sid,lon,lat,elv,wsp.90,wsp.80,wsp.70,wdir.0,wdir.50,wdir.8,t.8,pres.8,wsp.50,wsp.30,wsp.10
    call f%read(file_path, header_row=1, status_ok=status_ok)
    if (.not. status_ok) then
      call log_error('Failed to read ' // trim(file_path) // '!')
    end if
    call f%get_header(columns, status_ok)
    if (columns(1) /= 'time') then
      call log_error('Failed to get time column in ' // trim(file_path) // '!')
    else
      call f%get(1, time_col, status_ok)
    end if
    if (columns(2) /= 'sid') then
      call log_error('Failed to get sid column in ' // trim(file_path) // '!')
    else
      call f%get(2, sid_col, status_ok)
    end if
    if (columns(3) == 'lon') then
      call f%get(3, lon_col, status_ok)
    else if (columns(4) == 'lon') then
      call f%get(4, lon_col, status_ok)
    else
      call log_error('Failed to get lon column in ' // trim(file_path) // '!')
    end if
    if (columns(3) == 'lat') then
      call f%get(3, lat_col, status_ok)
    else if (columns(4) == 'lat') then
      call f%get(4, lat_col, status_ok)
    else
      call log_error('Failed to get lat column in ' // trim(file_path) // '!')
    end if
    if (columns(5) /= 'elv') then
      call log_error('Failed to get elv column in ' // trim(file_path) // '!')
    else
      call f%get(5, elv_col, status_ok)
    end if
    allocate(consumed(6:size(columns)))
    consumed = .false.
    i_ws = 0
    i_wd = 0
    i_ta = 0
    i_p  = 0
    i_rh = 0
    do i = 6, size(columns)
      if (consumed(i)) cycle
      idot = index(columns(i), '.')
      select case (columns(i)(1:idot-1))
      case ('wsp')
        i_ws = i
      case ('wdir')
        i_wd = i
      case ('t')
        i_ta = i
      case ('pres')
        i_p  = i
      case ('rh')
        i_rh = i
      end select
      h = to_int(columns(i)(idot+1:len_trim(columns(i))))
      ! Find other obs on the same height.
      do j = i + 1, size(columns)
        idot = index(columns(j), '.')
        if (h == to_int(columns(j)(idot+1:len_trim(columns(j))))) then
          select case (columns(j)(1:idot-1))
          case ('wsp')
            i_ws = j
          case ('wdir')
            i_wd = j
          case ('t')
            i_ta = j
          case ('pres')
            i_p  = j
          case ('rh')
            i_rh = j
          end select
          consumed(j) = .true.
        end if
      end do
      if (i_ws /= 0) call f%get(i_ws, ws_col, status_ok)
      if (i_wd /= 0) call f%get(i_wd, wd_col, status_ok)
      if (i_ta /= 0) call f%get(i_ta, ta_col, status_ok)
      if (i_p  /= 0) call f%get(i_p , p_col , status_ok)
      if (i_rh /= 0) call f%get(i_rh, rh_col, status_ok)
      if (i_ws /= 0 .and. i_wd /= 0) then
        do j = 1, size(time_col)
          ! Create tower object if needed.
          if (towers%hashed(sid_col(j))) then
            select type (value => towers%value(sid_col(j)))
            type is (anem_tower_type)
              tower => value
            end select
          else
            allocate(tower)
            call tower%init(sid_col(j), real(lon_col(j)), real(lat_col(j)), real(elv_col(j)))
            tower%seq_id = towers%size
            call towers%insert(sid_col(j), tower)
          end if
          call time%init(time_col(j), '%Y-%m-%d %H:%M:%S%Z')
          if (time%minute /= 0) cycle
          if (cli_start_time%year == 0 .or. (cli_start_time <= time .and. time <= cli_end_time)) then
            allocate(record)
            record%tower => tower
            record%time = time
            record%h = h
            if (i_ws /= 0) record%ws_avg = ws_col(j)
            if (i_wd /= 0) record%wd_avg = wd_col(j)
            if (i_ws /= 0 .and. i_wd /= 0 .and. record%ws_avg /= 0 .and. record%wd_avg /= 0) then
              record%ua_avg = wind_u_component(record%ws_avg, record%wd_avg)
              record%va_avg = wind_v_component(record%ws_avg, record%wd_avg)
            end if
            ! Here we drop temperature, pressure and relative humidity for the time being.
            call records%insert(time%isoformat(), record)
          end if
        end do
      end if
      consumed(i) = .true.
      i_ws = 0
      i_wd = 0
      i_ta = 0
      i_p  = 0
      i_rh = 0
      if (allocated(ws_col)) deallocate(ws_col)
      if (allocated(wd_col)) deallocate(wd_col)
      if (allocated(ta_col)) deallocate(ta_col)
      if (allocated(p_col )) deallocate(p_col )
      if (allocated(rh_col)) deallocate(rh_col)
    end do
    call f%destroy()
    if (allocated(columns )) deallocate(columns )
    if (allocated(time_col)) deallocate(time_col)
    if (allocated(sid_col )) deallocate(sid_col )
    if (allocated(lon_col )) deallocate(lon_col )
    if (allocated(lat_col )) deallocate(lat_col )
    if (allocated(elv_col )) deallocate(elv_col )
    if (allocated(ws_col  )) deallocate(ws_col  )
    if (allocated(wd_col  )) deallocate(wd_col  )
    if (allocated(ta_col  )) deallocate(ta_col  )
    if (allocated(p_col   )) deallocate(p_col   )
    if (allocated(rh_col  )) deallocate(rh_col  )

    call log_notice('Tower size is ' // trim(to_str(towers%size)) // ', record size is ' // trim(to_str(records%size)) // '.')

  end subroutine anem_csv_read

end module anem_csv_mod
